package com.liang.subject.domain.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.liang.subject.common.entity.PageDTO;
import com.liang.subject.common.util.IdWorkerUtil;
import com.liang.subject.common.util.LoginUtil;
import com.liang.subject.domain.convert.SubjectInfoConverter;
import com.liang.subject.domain.entity.SubjectInfoBO;
import com.liang.subject.domain.entity.SubjectOptionBO;
import com.liang.subject.domain.handler.subject.SubjectTypeHandler;
import com.liang.subject.domain.handler.subject.SubjectTypeHandlerFactory;
import com.liang.subject.domain.service.SubjectInfoDomainService;
import com.liang.subject.domain.service.SubjectLikedDomainService;
import com.liang.subject.infra.basic.entity.SubjectInfo;
import com.liang.subject.infra.basic.entity.SubjectLabel;
import com.liang.subject.infra.basic.entity.SubjectMapping;
import com.liang.subject.infra.basic.service.SubjectInfoService;
import com.liang.subject.infra.basic.service.SubjectMappingService;
import com.liang.subject.infra.entity.UserInfo;
import com.liang.subject.infra.es.entity.SubjectInfoEs;
import com.liang.subject.infra.es.service.SubjectEsService;
import com.liang.subject.infra.rpc.UserRpc;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class SubjectInfoDomainServiceImpl implements SubjectInfoDomainService {

    private final SubjectInfoService subjectInfoService;
    private final SubjectTypeHandlerFactory subjectTypeHandlerFactory;
    private final SubjectMappingService subjectMappingService;
    private final SubjectEsService subjectEsService;
    private final SubjectLikedDomainService subjectLikedDomainService;
    private final UserRpc userRpc;
    private final RedisTemplate<String, Object> redisTemplate;

    String key = "subject_rank";

    @Override
    @Transactional
    public void save(SubjectInfoBO subjectInfoBO) {
        log.info("SubjectInfoDomainServiceImpl.add.bo:{}", JSON.toJSONString(subjectInfoBO));
        SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBO2PO(subjectInfoBO);
        subjectInfoService.save(subjectInfo);
        SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());
        subjectInfoBO.setId(subjectInfo.getId());
        handler.save(subjectInfoBO);
        List<Long> categoryIds = subjectInfoBO.getCategoryIds();
        List<Long> labelIds = subjectInfoBO.getLabelIds();
        List<SubjectMapping> subjectMappingList = new ArrayList<>();
        categoryIds.forEach(categoryId -> {
            labelIds.forEach(labelId -> {
                SubjectMapping subjectMapping = new SubjectMapping();
                subjectMapping.setSubjectId(subjectInfo.getId());
                subjectMapping.setCategoryId(categoryId);
                subjectMapping.setLabelId(labelId);
                subjectMappingList.add(subjectMapping);
            });
        });
        subjectMappingService.saveBatch(subjectMappingList);
        // 题目信息同步到ES
        SubjectInfoEs subjectInfoEs = SubjectInfoEs.builder()
                .docId(new IdWorkerUtil(1, 1, 1).nextId())
                .subjectId(subjectInfo.getId())
                .subjectName(subjectInfo.getSubjectName())
                .subjectAnswer(subjectInfoBO.getSubjectAnswer())
                .subjectType(subjectInfo.getSubjectType())
                .createUser(subjectInfo.getCreatedBy())
                .createTime(new Date().getTime())
                .build();
        subjectEsService.insert(subjectInfoEs);
        redisTemplate.opsForZSet().add(key, LoginUtil.getLoginId(), 1);
    }

    @Override
    public PageDTO<SubjectInfoBO> querySubjectList(SubjectInfoBO subjectInfoBO) {
        Page<SubjectInfo> page = subjectInfoBO.toMpPage();
        SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBO2PO(subjectInfoBO);
        Long categoryId = subjectInfoBO.getCategoryId();
        Long labelId = subjectInfoBO.getLabelId();
        Page<SubjectInfo> subjectInfoList = subjectInfoService.querySubjectList(page, subjectInfo, categoryId, labelId);
        return PageDTO.of(subjectInfoList, info -> {
            SubjectInfoBO bo = SubjectInfoConverter.INSTANCE.convertPO2BO(info);
            return bo;
        });
    }

    @Override
    public SubjectInfoBO querySubjectInfo(SubjectInfoBO subjectInfoBO) {
        // 获取题目id
        Long subjectId = subjectInfoBO.getId();
        SubjectInfo subjectInfo = subjectInfoService.getById(subjectId);
        Integer subjectType = subjectInfo.getSubjectType();
        SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectType);
        SubjectOptionBO subjectOptionBO = handler.querySubjectInfo(subjectId);
        SubjectInfoBO bo = SubjectInfoConverter.INSTANCE.convert2BO(subjectInfo, subjectOptionBO);
        List<SubjectLabel> labelList = subjectMappingService.getLabelListBySubjectId(subjectId);
        List<String> labelNameList = labelList.stream().map(SubjectLabel::getLabelName).collect(Collectors.toList());
        bo.setLabelName(labelNameList);
        // 设置题目点赞信息
        Boolean liked = subjectLikedDomainService.isLiked(subjectId.toString(), LoginUtil.getLoginId());
        bo.setLiked(liked);
        Integer count = subjectLikedDomainService.getLikedCount(subjectId.toString());
        bo.setLikedCount(count);
        assembleSubjectCursor(subjectInfoBO, bo);
        return bo;
    }

    private void assembleSubjectCursor(SubjectInfoBO subjectInfoBO, SubjectInfoBO bo) {
        Long categoryId = subjectInfoBO.getCategoryId();
        Long labelId = subjectInfoBO.getLabelId();
        Long subjectId = subjectInfoBO.getId();
        if (Objects.isNull(categoryId) || Objects.isNull(labelId)) {
            return;
        }
        Long nextSubjectId = subjectInfoService.querySubjectIdCursor(subjectId, categoryId, labelId, 1);
        bo.setNextSubjectId(nextSubjectId);
        Long lastSubjectId = subjectInfoService.querySubjectIdCursor(subjectId, categoryId, labelId, 0);
        bo.setLastSubjectId(lastSubjectId);
    }

    @Override
    public PageDTO<SubjectInfoEs> getSubjectPageBySearch(SubjectInfoBO subjectInfoBO) {
        SubjectInfoEs subjectInfoEs = SubjectInfoEs.builder().keyword(subjectInfoBO.getKeyword()).build();
        subjectInfoEs.setPageNum(subjectInfoBO.getPageNum());
        subjectInfoEs.setPageSize(subjectInfoBO.getPageSize());
        return subjectEsService.querySubjectList(subjectInfoEs);
    }

    @Override
    public List<SubjectInfoBO> getContributeList() {
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, 5);
//        List<SubjectInfo> subjectInfoList = subjectInfoService.getContributeList();
        if (CollUtil.isEmpty(typedTuples)) {
            return Collections.emptyList();
        }
        List<SubjectInfoBO> subjectInfoBOList = new ArrayList<>();
        typedTuples.forEach(subjectInfo -> {
            SubjectInfoBO subjectInfoBO = new SubjectInfoBO();
            UserInfo userInfo = userRpc.getUserInfo((String) subjectInfo.getValue());
            subjectInfoBO.setCreateUserAvatar(userInfo.getAvatar());
            subjectInfoBO.setCreateUser(userInfo.getNickname());
            subjectInfoBO.setSubjectCount(subjectInfo.getScore().intValue());
            subjectInfoBOList.add(subjectInfoBO);
        });
        return subjectInfoBOList;
    }
}
